﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Text;
using System.Timers;
using System.Xml.Linq;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using nwrole.lib;

namespace nwrole.Supervisor.WorkerRole {
    //[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    //public class AzureSupervisor : ISupervisorContract {
    //    //public static string Identifier = "nwrole.SupervisorService";
    //    //public static string ServiceDisplayName = "nwrole Supervisor";
    //    //public static string ApplicationDirectory = "nwrole";

    //    private IOrchestratorChannel orchestratorChannel;
    //    private int SupervisorPort;

    //    private Timer masterConnectionTimer = new Timer();

    //    private List<ServiceData> ServiceDatas = new List<ServiceData>();

    //    private SupervisorData supervisorData;

    //    public AzureSupervisor(int supervisorPort) {

    //        SupervisorPort = supervisorPort;

    //        masterConnectionTimer.Elapsed += OnMasterConnectionTimer;
    //        masterConnectionTimer.Interval = 60 * 1000;

    //        supervisorData = new SupervisorData();
    //    }

    //    #region Supervisor
    //    public void Start(nwrole.lib.IServiceProvider serviceProvider, string pluginProviderClassName) {
    //        WriteLog("Start");
    //        EventLogger.LogInformation("Supervisor", "Supervisor starting");
    //        try {
    //            //Configuration.Load();
    //            string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    //            string supervisorConfig = Path.Combine(appDir, "supervisor-config.xml");
    //            if (File.Exists(supervisorConfig)) {
    //                PropertyList plist = new PropertyList(supervisorConfig);
    //                supervisorData.Name = plist["Name"] as string;
    //                supervisorData.Id = new Guid(plist["Id"] as string);
    //            } else {
    //                PropertyList plist = new PropertyList();
    //                plist["Name"] = Environment.MachineName;
    //                plist["Id"] = Guid.NewGuid();
    //                plist.Save(supervisorConfig);
    //            }
                
    //            //////////////////////
    //            string applicationDirectory = appDir;
    //            string servicesDir = Path.Combine(applicationDirectory, "services");

    //            CloudBlobContainer blobContainer = GetBlobContainer("services");

    //            foreach(IListBlobItem blobItem in blobContainer.ListBlobs()) {
    //                CloudBlob blob = blobItem as CloudBlob;
    //                if(blob != null && blob.Uri.ToString().EndsWith(".zip",StringComparison.OrdinalIgnoreCase)) {
    //                    string fileName = Path.GetFileName(blob.Uri.ToString());
    //                    string serviceName = fileName.Remove(fileName.Length - 4);

    //                    string localFilePath = Path.Combine(servicesDir, fileName);
                        
    //                    blob.FetchAttributes();
    //                    DateTime dateStamp = blob.Properties.LastModifiedUtc;
    //                    //DateTime dateStamp = DateTime.Parse(blob.Metadata["Date"]);

    //                    bool download = true;

    //                    if (File.Exists(localFilePath)) {
    //                        download = false;
    //                        DateTime fileDate = File.GetCreationTimeUtc(localFilePath);
    //                        if (fileDate > dateStamp) {
    //                            File.Delete(localFilePath);
    //                            download = true;
    //                        }
    //                    }

    //                    if(download) {
    //                        string servicePath = Path.Combine(servicesDir, serviceName);

    //                        blob.DownloadToFile(localFilePath);
    //                        File.SetCreationTimeUtc(localFilePath, dateStamp);

    //                        FileStream filestream = File.OpenRead(localFilePath);
    //                        UnpackZip(filestream, servicePath);
    //                    }

    //                    ServiceData serviceData = null;

    //                    PropertyList plist = LoadServiceSettings(serviceName);
    //                    if(plist != null) {
    //                        serviceData = StorageUtil.ServiceDataFromDictionary((IDictionary)plist.DictionaryForKey("Service"));
    //                    }
                        
    //                    if (serviceData == null) {
    //                        serviceData = new ServiceData();
    //                        serviceData.Name = serviceName;
    //                        serviceData.Id = Guid.NewGuid();
    //                        serviceData.Enabled = true;
    //                        //serviceData.PluginDatas = new List<PluginData>();

    //                        plist = new PropertyList();
    //                        plist["Service"] = StorageUtil.ServiceDataToDictionary(serviceData);

    //                        SaveServiceSettings(plist, serviceName);
    //                    }

    //                    serviceData.Id = new Guid("4be8ff89e1db45cf947db20d650c1bc2");
    //                    serviceData.ManagementPort = NextFreeManagementPort();

    //                    ServiceDatas.Add(serviceData);
    //                }
    //            }
                
    //            try {
    //                ConnectToOrchestrator();
    //            } catch (Exception e) {

    //            }

    //            foreach (var serviceData in ServiceDatas) {
    //                if (serviceData.Enabled)
    //                    DeployUtil.DeployService(serviceData, true, SupervisorPort, pluginProviderClassName);
    //            }

    //        } catch (Exception exception) {
    //            EventLogger.LogException("Supervisor.Start", exception);
    //        }
    //        EventLogger.LogInformation("Supervisor", "Supervisor started");
    //    }

    //    public void Stop() {
    //        EventLogger.LogInformation("Supervisor", "Supervisor stopping");
    //        foreach (var serviceData in ServiceDatas) {
    //            try {
    //                if (serviceData.Deployed)
    //                    DeployUtil.RetractService(serviceData, true);
    //            } catch (Exception exception) {
    //                EventLogger.LogException("Supervisor.Stop", exception);
    //            }
    //        }

    //        EventLogger.LogInformation("Supervisor", "Supervisor stopped");
    //    }
    //    #endregion

    //    #region ISupervisorContract
    //    public void StartService() {

    //    }

    //    public void StopService() {

    //    }

    //    public int GetNumServices() {
    //        return ServiceDatas.Count;
    //    }

    //    public SupervisorData GetSupervisorData() {
    //        return supervisorData;
    //    }

    //    public ServiceData[] GetServices() {
    //        return ServiceDatas.ToArray();
    //    }

    //    public void ReportError(string message) {
    //        WriteLog("Supervisor.ReportError: " + message);
    //        if (orchestratorChannel != null) {
    //            try {
    //                orchestratorChannel.ReportError(message);
    //            } catch (Exception e) {
    //                OrchestratorChannelDisconnected();
    //            }
    //        }
    //    }

    //    private static void WriteLog(string content) {
    //        try {
    //            string logDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "log/Supervisor/");
    //            if (!System.IO.Directory.Exists(logDir))
    //                System.IO.Directory.CreateDirectory(logDir);

    //            DateTime now = DateTime.Now;

    //            System.IO.File.AppendAllText(logDir + "log " + now.ToString("yyyy-MM-dd") + ".txt", now.ToString("yyyy-MM-dd HH:mm:ss ") + content + "\r\n");
    //        }
    //        catch(Exception e){
    //        }
    //    }

    //    #endregion

    //    public void ConnectToOrchestrator() {
    //        try {
    //            ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect;

    //            Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", "systemevolutiontestfive","nwrole/Orchestrator");

    //            //Console.WriteLine("Connecting to {0} address: {1}", DeployContext.Config.DeployName, serviceUri);

    //            TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
    //            sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
    //            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = "owner";
    //            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret =
    //                "ihXdEKIJtg8jByN1aITLDX99eiEgMeHOfr4ppzX5MfM=";

    //            //ChannelFactory<IOrchestratorChannel> channelFactory = new ChannelFactory<IOrchestratorChannel>();
    //            DuplexChannelFactory<IOrchestratorChannel> channelFactory =
    //                new DuplexChannelFactory<IOrchestratorChannel>(new OrchestratorClient(this));
    //            channelFactory.Endpoint.Address = new EndpointAddress(serviceUri);
    //            channelFactory.Endpoint.Binding = new NetTcpRelayBinding();
    //            channelFactory.Endpoint.Contract.ContractType = typeof(IOrchestratorChannel);

    //            channelFactory.Endpoint.Behaviors.Add(sharedSecretServiceBusCredential);

    //            orchestratorChannel = channelFactory.CreateChannel();
    //            orchestratorChannel.Open();

    //            orchestratorChannel.Faulted += orchestratorChannel_Faulted;
    //            orchestratorChannel.Closed += orchestratorChannel_Closed;

    //            orchestratorChannel.RegisterSupervisor();
    //        } catch (Exception e) {
    //            orchestratorChannel = null;
    //        }

    //        if (orchestratorChannel == null) {

    //            masterConnectionTimer.Start();
    //        }
    //    }

    //    void orchestratorChannel_Closed(object sender, EventArgs e) {
    //        Console.WriteLine("Supervisor: Orchestrator channel closed");
    //        OrchestratorChannelDisconnected();
    //    }

    //    void orchestratorChannel_Faulted(object sender, EventArgs e) {
    //        Console.WriteLine("Supervisor: Orchestrator channel faulted");
    //        OrchestratorChannelDisconnected();
    //    }

    //    private void OrchestratorChannelDisconnected() {
    //        orchestratorChannel = null;
    //        masterConnectionTimer.Start();
    //    }

    //    private void OnMasterConnectionTimer(object source, ElapsedEventArgs e) {
    //        masterConnectionTimer.Stop();
    //        ConnectToOrchestrator();
    //    }

    //    public int NextFreeManagementPort() {
    //        int port = 63000;
    //        bool success = true;
    //        do {
    //            port++;
    //            success = true;
    //            foreach (var serviceData in ServiceDatas) {
    //                if (serviceData.ManagementPort == port) {
    //                    success = false;
    //                    break;
    //                }
    //            }
    //        } while (!success);
    //        return port;
    //    }

    //    private CloudBlobClient GetBlobClient() {
    //        //var storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=<YOURACCOUNTNAME>;AccountKey=<YOURACCOUNTKEY>");
    //        var storageAccount = CloudStorageAccount.FromConfigurationSetting("AzureStorage.ConnectionString");
    //        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

    //        // For large file copies you need to set up a custom timeout period
    //        // and using parallel settings appears to spread the copy across multiple threads
    //        // if you have big bandwidth you can increase the thread number below
    //        // because Azure accepts blobs broken into blocks in any order of arrival.
    //        blobClient.Timeout = new System.TimeSpan(1, 0, 0);
    //        blobClient.ParallelOperationThreadCount = 2;

    //        return blobClient;
    //    }
    //    private CloudBlobContainer GetBlobContainer(string name) {

    //        CloudBlobClient blobClient = GetBlobClient();

    //        // Get and create the container
    //        CloudBlobContainer blobContainer = blobClient.GetContainerReference(name);
    //        blobContainer.CreateIfNotExist();

    //        // Setup the permissions on the container to be private
    //        var permissions = new BlobContainerPermissions();
    //        permissions.PublicAccess = BlobContainerPublicAccessType.Off;
    //        blobContainer.SetPermissions(permissions);

    //        return blobContainer;
    //    }

    //    public void SaveSupervisorSettings(PropertyList plist) {
    //        CloudBlobContainer blobContainer = GetBlobContainer("services");

    //        var blob = blobContainer.GetBlobReference("supervisor-config.xml");
    //        /*blob.FetchAttributes();

    //        MemoryStream settingStream = new MemoryStream();
    //        plist.Save(settingStream);
    //        StreamReader reader = new StreamReader(settingStream);
    //        string text = reader.ReadToEnd();*/

    //        BlobStream blobStream = blob.OpenWrite();
    //        plist.Save(blobStream);

    //        /*blob.Metadata["ServiceSettings"] = text;

    //        blob.SetMetadata();*/
    //    }

    //    public PropertyList LoadSupervisorSettings() {
    //        CloudBlobContainer blobContainer = GetBlobContainer("services");

    //        PropertyList plist = null;

    //        var blob = blobContainer.GetBlobReference("supervisor-config.xml");
    //        try {
    //            using (BlobStream blobStream = blob.OpenRead()) {
    //                plist = new PropertyList(blobStream);
    //            }
    //        } catch (Exception e) {
    //            return null;
    //        }

    //        return plist;
    //    }

    //    public void SaveServiceSettings(PropertyList plist, string serviceName) {
    //        CloudBlobContainer blobContainer = GetBlobContainer("services");

    //        string filename = serviceName + ".zip";

    //        var blob = blobContainer.GetBlobReference(filename);
    //        blob.FetchAttributes();

    //        /*MemoryStream settingStream = new MemoryStream();
    //        plist.Save(settingStream);
    //        settingStream.Position = 0;
    //        StreamReader reader = new StreamReader(settingStream);
    //        string text = reader.ReadToEnd();*/
    //        string text = plist.ToXml();

    //        blob.Metadata["ServiceSettings"] = System.Web.HttpUtility.HtmlEncode(text);

    //        blob.SetMetadata();
    //    }

    //    public PropertyList LoadServiceSettings(string serviceName) {
    //        CloudBlobContainer blobContainer = GetBlobContainer("services");

    //        string filename = serviceName + ".zip";

    //        var blob = blobContainer.GetBlobReference(filename);
    //        blob.FetchAttributes();

    //        string settings = blob.Metadata["ServiceSettings"];
    //        if (settings == null)
    //            return null;

    //        /*MemoryStream settingStream = new MemoryStream(Encoding.ASCII.GetBytes(settings));
    //        PropertyList plist = new PropertyList(settingStream);*/
    //        PropertyList plist = PropertyList.LoadData(System.Web.HttpUtility.HtmlDecode(settings));

    //        return plist;
    //    }

    //    private void UnpackZip(Stream stream, string destPath) {

    //        //string StorageDirectoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    //        //Directory.CreateDirectory(StorageDirectoryPath);
    //        Directory.CreateDirectory(destPath);

    //        using (ZipInputStream zipStream = new ZipInputStream(stream)) {
    //            ZipEntry entry;
    //            while ((entry = zipStream.GetNextEntry()) != null) {
    //                /*if (!String.IsNullOrEmpty(entry.Name) && entry.Name.Equals(@"solution.plist", StringComparison.OrdinalIgnoreCase)) {
    //                    MemoryStream dataStream = new MemoryStream();
    //                    byte[] buffer = new byte[8192];
    //                    int size = zipStream.Read(buffer, 0, buffer.Length);
    //                    while (size > 0) {
    //                        dataStream.Write(buffer, 0, size);
    //                        size = zipStream.Read(buffer, 0, buffer.Length);
    //                    };
    //                    dataStream.Seek(0, SeekOrigin.Begin);
    //                    PropertyList plist = new PropertyList(dataStream);
    //                    Id = new Guid(plist.StringForKey("id"));
    //                    Name = plist.StringForKey("name");
    //                    Description = plist.StringForKey("description");
    //                    Author = plist.StringForKey("author");
    //                    Created = plist.DateTimeForKey("created");
    //                    Updated = plist.DateTimeForKey("updated");
    //                    foreach (object dict in plist.ArrayForKey("services"))
    //                        Services.Add(new Service((IDictionary)dict));
    //                } else*/
    //                {
    //                    using (FileStream fileStream = File.Create(Path.Combine(destPath, entry.Name))) {
    //                        byte[] buffer = new byte[8192];
    //                        int size;
    //                        while ((size = zipStream.Read(buffer, 0, buffer.Length)) > 0)
    //                            fileStream.Write(buffer, 0, size);
    //                        fileStream.Close();
    //                    }
    //                    //Files.Add(Path.Combine(StorageDirectoryPath, entry.Name));
    //                }
    //            }
    //        }
    //    }

    //}
}
